(function($){
    // boostrap style spinner plugin
    var Spinner = function(element, options) {
        this.init('spinner', element, options);
    };
    Spinner.prototype = {
        constructor: Spinner,
        init: function(type, element, options) {
            this.type = type;
            this.timer = null;
            this.$element = $(element);
            this.options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data());
            this.createButtons();
            this._bindButtons();
            this.$element.bind('keypress', function(e) {
                var i, validKeys = '0123456789\n';
                for(i=0; i<validKeys.length; i++) {
                    if(e.charCode === validKeys[i].charCodeAt(0)) {
                        return true;
                    }
                }
                return false;
            });
        },
        createButtons: function() {
            // if you want custom buttons just override this method
            this.$upButton = $('<span>+</span>').attr({
                'class': 'spinner-up'
            }).insertAfter(this.$element);
            this.$downButton = $('<span>-</span>').attr({
                'class': 'spinner-down'
            }).insertBefore(this.$element);
        },
        _bindButtons: function() {
            var self = this;
            self.$upButton.add(self.$downButton)
                          .mousedown(function(e) {
                              self._mouseDown(this);
                              return false;
                           })
                          .mouseup(function(e) {
                              self._mouseUp(this);
                              return false;
                           })
                          .mouseout(function(e) {
                              self._mouseUp(this);
                              return false;
                           });
        },
        _mouseDown: function(button) {
            var self = this;
            if(this.$element.attr('disabled') !== undefined) {
                return;
            }
            var direction = (button === this.$upButton.get(0) ? 1 : -1);
            this.$element.focus();
            this.$element.select();

            this._spin(direction);
            if(!this.timer) {
                this.timer = setInterval(function() {
                    self._spin(direction);
                }, this.options.delay);
            }
        },
        _spin: function(direction) {
            var newValue,
                currentValue = parseInt(this.$element.val(), '');
            if(isNaN(currentValue)) {
                currentValue = this.options.min || 0;
            }
            newValue = currentValue + direction;
            newValue = (this.options.min !== undefined && newValue < this.options.min)? this.options.min : newValue;
            newValue = (this.options.max !== undefined && newValue > this.options.max)? this.options.min : newValue;

            if(newValue !== currentValue) {
                this.$element.val(newValue);
            }
        },
        _mouseUp: function(button) {
            if(this.timer) {
                clearInterval(this.timer);
                this.timer = null;
                this.$element.change();
            }
        }
    };
    $.fn.spinner = function ( option ) {
        return this.each(function () {
            var $this = $(this),
                data = $this.data('spinner'),
                options = typeof option == 'object' && option;

            if(!data) {
                data = new Spinner(this, options);
                $this.data('spinner', data);
            }
            if(typeof option == 'string') {
                data[option]();
            }
        });
    };
    $.fn.spinner.Constructor = Spinner;
    $.fn.spinner.defaults = {
        delay: 200
    };
})(window.jQuery);
